﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;

/// <summary>
/// Summary description for NeedlemanWunschMMF
/// </summary>
public class NeedlemanWunschMMF
{
	
    
    //Declarações de matrizes que sumiram com o novo método.
    public char[] mSeqA { get; set; }
    public char[] mSeqB { get; set; }

    public int mScore { get; set; }
    public String mAlignmentSeqA { get; set; }
    public String mAlignmentSeqB { get; set; }
    public MemoryMappedMatrix Matrix;
    private int cellsize;
    //Método construtor da classe. O construtor recebe como parâmetro as duas sequências de nucleotídeos.
    public NeedlemanWunschMMF(char[] seqA, char[] seqB)
    {

        //Inicializando os parâmetros
        this.mAlignmentSeqA = "";
        this.mAlignmentSeqB = "";
        this.mSeqA = seqA;
        this.mSeqB = seqB;
        this.cellsize = Math.Max(mSeqA.Length, mSeqB.Length).ToString().Length + 2;
        Matrix = new MemoryMappedMatrix(cellsize, seqA.Length+1, seqB.Length+1, AppDomain.CurrentDomain.BaseDirectory);

    }

    //Este método executa o algoritmo nas sequências do objeto.
    public void executar()
    {
        processar();
        backtrack();
    }

    //Este método preenche a matriz de score utilizando as funções de maximização do scoreDiag, scoreLeft e scoreUp.
    public void processar()
    {
        int score = 0;
        StringBuilder lineBuilder = new StringBuilder();

        long[] lastline = new long[mSeqB.Length + 1];
        long[] thisline = new long[mSeqB.Length + 1];
        for (int i = 0; i <= this.mSeqA.Length; i++)
        {

            thisline = new long[mSeqB.Length + 1];
            for (int j = 0; j <= this.mSeqB.Length; j++)
            {
                if (i == 0)
                {
                    int aux0 = -j;
                    String aux = aux0.ToString();
                    while (aux.Length < cellsize)
                    {
                        aux += 'X';
                    }
                    lineBuilder.Append(aux);
                    thisline[j] = -j;
                }
                else if (j == 0)
                {
                    int aux1 = -i;
                    String aux2 = aux1.ToString();
                    while (aux2.Length < cellsize)
                    {
                        aux2 += 'X';
                    }
                    lineBuilder.Append(aux2);
                    thisline[j] = -i;
                }
                else
                {

                    int scoreDiag = Convert.ToInt32(lastline[j - 1]) + weight(i, j); //this.mD[i - 1, j - 1] + weight(i, j);
                    int scoreLeft = Convert.ToInt32(thisline[j - 1]) - 1; //this.mD[i, j - 1] - 1;
                    int scoreUp = Convert.ToInt32(lastline[j]) -1; //this.mD[i - 1, j] - 1;
                    score = Math.Max(Math.Max(scoreDiag, scoreLeft), scoreUp);
                    String Sscore = score.ToString();
                    while (Sscore.Length < cellsize)
                    {
                        Sscore += 'X';
                    }
                    lineBuilder.Append(Sscore);
                    thisline[j] = score;
                    

                }
            }

            lastline = thisline;
            Matrix.setLineByOffset(i, 0, lineBuilder.ToString());
            lineBuilder.Clear();
        }


        

    }

    //Este método calcula e gera o alinhamento de sequências.
    public void backtrack()
    {
        int i = this.mSeqA.Length;
        int j = this.mSeqB.Length;
        this.mScore = Convert.ToInt32(Matrix.getPosition(i,j).Split('X')[0]);

        while (i > 0 && j > 0)
        {
            int nowscore = Convert.ToInt32(Matrix.getPosition(i, j).Split('X')[0]);
            if (nowscore == Convert.ToInt32(Matrix.getPosition(i - 1, j - 1).Split('X')[0]) + weight(i, j))
            {
                this.mAlignmentSeqA += this.mSeqA[i - 1];
                this.mAlignmentSeqB += this.mSeqB[j - 1];
                i--;
                j--;
                continue;
            }
            else if (nowscore == Convert.ToInt32(Matrix.getPosition(i, j - 1).Split('X')[0]) - 1)
            {
                this.mAlignmentSeqA += "-";
                this.mAlignmentSeqB += this.mSeqB[j - 1];
                j--;
                continue;
            }
            else
            {
                this.mAlignmentSeqA += this.mSeqA[i - 1];
                this.mAlignmentSeqB += "-";
                i--;
                continue;
            }
        }

        char[] mA = this.mAlignmentSeqA.ToCharArray();
        char[] mB = this.mAlignmentSeqB.ToCharArray();
        Array.Reverse(mA);
        Array.Reverse(mB);
        this.mAlignmentSeqA = new String(mA);
        this.mAlignmentSeqB = new String(mB);

        Matrix.DeleteFile();
    }

    //Este método calcula o match ou mismatch.
    private int weight(int i, int j)
    {
        if (this.mSeqA[i - 1] == this.mSeqB[j - 1])
        {
            return 1;
        }
        else
        {
            return -1;
        }
    }

}